/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.service.document.report;

import cn.ac.nercis.pes.common.constant.common.ReportConstants;
import cn.ac.nercis.pes.common.constant.doc.*;
import cn.ac.nercis.pes.common.constant.evaluation.EvaluationStage;
import cn.ac.nercis.pes.common.constant.evaluation.ReportType;
import cn.ac.nercis.pes.common.utils.DataUtils;
import cn.ac.nercis.pes.model.bo.doc.common.evaluation.NetworkTopologyBO;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.LastEvaluationBO;
import cn.ac.nercis.pes.model.bo.doc.report.evaluation.SystemSituationBO;
import cn.ac.nercis.pes.model.bo.doc.report.system.*;
import cn.ac.nercis.pes.model.bo.doc.report.table.SecurityThreatReportBO;
import cn.ac.nercis.pes.model.bo.doc.report.unit.EvaluationUnitBO;
import cn.ac.nercis.pes.model.bo.doc.report.unit.TestedUnitBO;
import cn.ac.nercis.pes.model.dto.common.ResultCode;
import cn.ac.nercis.pes.model.dto.evaluation.content.LastEvaluationDTO;
import cn.ac.nercis.pes.model.dto.evaluation.content.NetworkTopologyDTO;
import cn.ac.nercis.pes.model.dto.evaluation.prepare.SecurityThreatDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationProcessRepository;
import cn.ac.nercis.pes.repository.dal.manage.EvaluationUnitRepository;
import cn.ac.nercis.pes.repository.dal.manage.TestedUnitRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.dal.report.EvaluationReportRepository;
import cn.ac.nercis.pes.repository.mapper.manage.EvaluationUnitMapper;
import cn.ac.nercis.pes.repository.mapper.manage.PasswordConditionMapper;
import cn.ac.nercis.pes.repository.mapper.manage.TestedUnitMapper;
import cn.ac.nercis.pes.repository.model.manage.EvaluationUnitDO;
import cn.ac.nercis.pes.repository.model.manage.TestedUnitDO;
import cn.ac.nercis.pes.repository.model.project.ProjectDO;
import cn.ac.nercis.pes.repository.model.project.SystemDO;
import cn.ac.nercis.pes.repository.model.project.SystemEvaluationDO;
import cn.ac.nercis.pes.repository.model.report.EvaluationReportDO;
import cn.ac.nercis.pes.service.document.common.DocDataService;
import cn.ac.nercis.pes.service.document.common.DocTargetDataService;
import cn.ac.nercis.pes.service.evaluation.*;
import cn.ac.nercis.pes.service.utils.FileManage;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

/**
 * 密评报告数据服务类
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public record ReportDataService(EvaluationProcessService evaluationProcessService,
                                EvaluationUnitRepository evaluationUnitRepository,
                                TestedUnitRepository testedUnitRepository,
                                EvaluationContentService evaluationContentService,
                                PasswordServerService passwordServerService,
                                SecurityThreatService securityThreatService,
                                EvaluationToolsService evaluationToolsService,
                                EvaluationReportRepository evaluationReportRepository,
                                EvaluationProcessRepository evaluationProcessRepository,
                                SystemTargetRepository systemTargetRepository,
                                ReportAssetDataService reportAssetDataService,
                                DocTargetDataService docTargetDataService,
                                DocDataService docDataService) {
    private static final DateTimeFormatter LOCAL_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy年MM月dd日");
    @Autowired
    public ReportDataService {
    }

    private String toString(LocalDate localDate) {
        if (Objects.isNull(localDate)) {
            return "";
        }
        return LOCAL_DATE_FORMATTER.format(localDate);
    }

    public String getReportCode(@NonNull SystemDO systemDO) {
        try {
            return DataUtils.findFirst(evaluationReportRepository.findBySystemIdAndReportType(systemDO.getId(), ReportType.REPORT))
                    .map(EvaluationReportDO::getCode)
                    .orElse(defaultReportCode(systemDO));
        } catch (RuntimeException rEx) {
            log.error("{}/getReportCode 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }
    private String defaultReportCode(SystemDO systemDO) {
        //项目编号+系统顺序号
        String reportCode = String.format("%s-%02d", systemDO.getProject().getNumber(), systemDO.getSort());
        return ReportConstants.PES_REPORT_CODE_PREFIX.concat(reportCode);
    }

    public TestedUnitBO getTestedUnit(@NonNull SystemDO systemDO) throws Exception {
        try {
            TestedUnitDO testedUnitDO = testedUnitRepository.findById(systemDO.getProject().getTestedUnitId()).orElse(null);
            if (Objects.isNull(testedUnitDO)) {
                throw new Exception(ResultCode.TESTED_UNIT_NOT_EXIST.getMsg());
            }
            return TestedUnitMapper.INSTANCE.toTestedUnitBO(testedUnitDO);
        } catch (Exception rEx) {
            log.error("{}/getTestedUnit 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public String getTestedUnitSystemName(@NonNull SystemDO systemDO) {
        try {
            return systemDO.getProject().getTestedUnit().concat(systemDO.getName());
        } catch (RuntimeException rEx) {
            log.error("{}/getTestedUnitSystemName 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public String getEntrustedUnitName(@NonNull SystemDO systemDO) {
        try {
            return Optional.of(systemDO.getProject())
                    .map(ProjectDO::getRequesterUnit)
                    .orElse("");
        } catch (RuntimeException rEx) {
            log.error("{}/getEntrustedUnitName 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public String getReportDateInDay(@NonNull SystemDO systemDO) {
        var reportPlan = evaluationProcessRepository.findBySystemIdAndName(systemDO.getId(), EvaluationStage.REPORT.getName())
                .stream()
                .findFirst()
                .orElse(null);
        if (Objects.nonNull(reportPlan) && Objects.nonNull(reportPlan.getEndDate())) {
            return LOCAL_DATE_FORMATTER.format(reportPlan.getEndDate());
        } else {
            return LOCAL_DATE_FORMATTER.format(LocalDate.now());
        }
    }

    public String getDistributionRange(@NonNull SystemDO systemDO) {
        try {
            return DataUtils.findFirst(evaluationReportRepository.findBySystemIdAndReportType(systemDO.getId(), ReportType.REPORT))
                    .map(EvaluationReportDO::getDistributionRange)
                    .orElse(defaultDistributionRange());
        } catch (RuntimeException rEx) {
            log.error("{}/getDistributionRange 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    private String defaultDistributionRange() {
        return "本报告一式三份，其中一份提交密码管理部门，一份提交被测单位，一份由密评机构留存。";
    }
    public EvaluationUnitBO getEvaluationUnit() throws Exception {
        try {
            EvaluationUnitDO evaluationUnitDO = this.evaluationUnitRepository.findAll().stream()
                    .findFirst()
                    .orElse(null);
            if (Objects.isNull(evaluationUnitDO)) {
                throw new Exception(ResultCode.EVALUATION_UNIT_NOT_EXIST.getMsg());
            }
            return EvaluationUnitMapper.INSTANCE.toEvaluationUnitBO(evaluationUnitDO);
        } catch (RuntimeException rEx) {
            log.error("{}/getEvaluationUnit 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public SystemBO getSystem(@NonNull SystemDO systemDO, @NonNull SystemEvaluationDO evaluationDO) {
        try {
            var isChange = Objects.nonNull(evaluationDO.getIsChange()) ? evaluationDO.getIsChange() : false;
            SystemBO systemBO = SystemBO.builder()
                    .name(systemDO.getName())
                    .level(systemDO.getSecurityLevel().getName())
                    .selectLevel(StringUtils.isBlank(systemDO.getReqLevel()))
                    .selectCii(StringUtils.equals(evaluationDO.getCiiState(), CiiState.FINISH.getName()))
                    .introduction(StringUtils.isEmpty(systemDO.getDesc()) ? " " : systemDO.getDesc())
                    .serviceUserCount(evaluationDO.getServiceUserCount())
                    .build();
            if (systemBO.getSelectLevel()) {
                systemBO.setDefLevel(systemDO.getSecurityLevel().getName());
                systemBO.setSLevel(String.valueOf(systemDO.getSLevel()));
                systemBO.setALevel(String.valueOf(systemDO.getALevel()));
                systemBO.setGLevel(String.valueOf(systemDO.getGLevel()));
                systemBO.setFilingNumber(systemDO.getFilingNumber());
                systemBO.setSelectSame(isChange);
                if (!isChange) {
                    systemBO.setChangeDesc(evaluationDO.getChangeDesc());
                }
            }else{
                systemBO.setReqLevel(systemDO.getReqLevel());
            }
            if (systemBO.getSelectCii()) {
                systemBO.setCiiDesc(evaluationDO.getCiiDesc());
            }
            return systemBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getSystem 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public GradeProtectionBO getGradeProtection(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            GradeProtectionBO gradeProtectionBO = GradeProtectionBO.builder()
                    .state(evaluationDO.getGradeProtectionState())
                    .build();
            if (StringUtils.equals(gradeProtectionBO.getState(),GradeProtectionState.FINISH.getName())) {
                gradeProtectionBO.setUnitName(evaluationDO.getEvaluationUnitName());
                gradeProtectionBO.setEvaluationDate(this.toString(evaluationDO.getEvaluationDate()));
                gradeProtectionBO.setConclusion(evaluationDO.getConclusion());
            } else if (StringUtils.equals(gradeProtectionBO.getState(),GradeProtectionState.RUN.getName())) {
                gradeProtectionBO.setUnitName(evaluationDO.getEvaluationUnitName());
            }
            return gradeProtectionBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getGradeProtection 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public ServiceConditionBO getServiceCondition(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            ServiceConditionBO conditionBO = ServiceConditionBO.builder()
                    .serviceRange(evaluationDO.getServiceRange())
                    .serviceObject(evaluationDO.getServiceObject())
                    .build();

            if (StringUtils.equals(conditionBO.getServiceRange(),ServiceConditionItems.INTER_PROVINCIAL.getName())) {
                conditionBO.setInterProvincialCount(String.valueOf(evaluationDO.getInterProvincialCount()));
            }
            if (StringUtils.equals(conditionBO.getServiceRange(),ServiceConditionItems.INTER_AREA.getName())) {
                conditionBO.setInterAreaCount(String.valueOf(evaluationDO.getInterAreaCount()));
            }
            if (StringUtils.equals(conditionBO.getServiceRange(),ServiceConditionItems.OTHER_RANGE.getName())) {
                conditionBO.setRangeDesc(evaluationDO.getServiceRangeDesc());
            }
            if (StringUtils.equals(conditionBO.getServiceObject(),ServiceConditionItems.OTHER_OBJECT.getName())) {
                conditionBO.setObjectDesc(evaluationDO.getServiceObjectDesc());
            }
            return conditionBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getServiceCondition 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public ServiceAreaBO getServiceArea(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            String serviceScope = evaluationDO.getServiceScope();
            if (Objects.isNull(serviceScope)) {
                return ServiceAreaBO.builder().build();
            }
            ServiceAreaBO areaBO = new ServiceAreaBO();
            areaBO.setServiceScope(serviceScope);
            areaBO.setQtDesc(evaluationDO.getServiceScopeDesc());
            return areaBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getServiceArea 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public NetworkPlatformBO getNetworkPlatform(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            return NetworkPlatformBO.builder()
                    .coverage(evaluationDO.getCoverage())
                    .coverageDesc(evaluationDO.getCoverageDesc())
                    .networkNature(evaluationDO.getNetworkNature())
                    .networkNatureDesc(evaluationDO.getNetworkNatureDesc())
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getNetworkPlatform 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public RunConditionBO getRunCondition(SystemEvaluationDO evaluationDO) {
        try {
            RunConditionBO conditionBO = RunConditionBO.builder()
                    .selectRun(evaluationDO.getIsRunning())
                    .build();
            if (evaluationDO.getIsRunning()) {
                conditionBO.setRunYear(evaluationDO.getRunYear());
                conditionBO.setRunMonth(evaluationDO.getRunMonth());
            } else {
                conditionBO.setRunDesc(evaluationDO.getRunDesc());
            }
            return conditionBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getRunCondition 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public InterconnectionBO getInterconnection(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            return InterconnectionBO.builder()
                    .interconnection(evaluationDO.getInterconnection())
                    .interconnectionDesc(evaluationDO.getInterconnectionDesc())
                    .systemName(evaluationDO.getNetName())
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getInterconnection 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public CloudSystemBO getCloudSystem(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            CloudSystemBO cloudSystemBO = CloudSystemBO.builder()
                    .selectCloud(StringUtils.equals(evaluationDO.getCloudState(), CloudState.SELECTED.getName()))
                    .build();
            if (cloudSystemBO.getSelectCloud()) {
                cloudSystemBO.setCloudName(evaluationDO.getCloudName());
                cloudSystemBO.setCloudSelectState(evaluationDO.getCloudSelectState());
                cloudSystemBO.setUnitName(evaluationDO.getCloudUnit());
                cloudSystemBO.setDate(this.toString(evaluationDO.getCloudDate()));
                cloudSystemBO.setConclusion(evaluationDO.getCloudCon());
            }
            return cloudSystemBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getCloudSystem 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public PASchemeBO getPAScheme(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            PASchemeBO schemeBO = PASchemeBO.builder()
                    .paScheme(evaluationDO.getPaScheme())
                    .build();
            if (StringUtils.equals(schemeBO.getPaScheme(),PaSchemeItems.PASS.getName())) {
                schemeBO.setPassDate(this.toString(evaluationDO.getPassDate()));
                schemeBO.setPaSchemeMethod(evaluationDO.getPaSchemeMethod());
                schemeBO.setUnit(evaluationDO.getPaSchemeUnit());
            }
            return schemeBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getPAScheme 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public PPSituationBO getPPSituation(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            PPSituationBO situationBO = PPSituationBO.builder()
                    .ppSituation(evaluationDO.getPpSituation())
                    .build();
            if (StringUtils.equals(situationBO.getPpSituation(),PpSituationItems.USE.getName())) {
                situationBO.setCount(DataUtils.toString(evaluationDO.getPpSituationCount()));
                situationBO.setIndependentCount(DataUtils.toString(evaluationDO.getPpSituationIndependentCount()));
                situationBO.setSharedCount(DataUtils.toString(evaluationDO.getPpSituationSharedCount()));
                situationBO.setCertifiedCount(DataUtils.toString(evaluationDO.getPpSituationCertifiedCount()));
                situationBO.setUncertifiedCount(DataUtils.toString(evaluationDO.getPpSituationUnCertifiedCount()));
                situationBO.setForeignCount(DataUtils.toString(evaluationDO.getPpSituationForeignCount()));
            }
            return situationBO;
        } catch (RuntimeException rEx) {
            log.error("{}/getPPSituation 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public UseAlgorithmBO getUseAlgorithm(@NonNull SystemEvaluationDO evaluationDO) {
        try {
            return UseAlgorithmBO.builder()
                            .groupingAlgorithm(evaluationDO.getGroupingAlgorithm())
                            .groupingAlgorithmDesc(evaluationDO.getGroupingAlgorithmDesc())
                            .asymmetricAlgorithm(evaluationDO.getAsymmetricAlgorithm())
                            .asymmetricAlgorithmDesc(evaluationDO.getAsymmetricAlgorithmDesc())
                            .hashAlgorithm(evaluationDO.getHashAlgorithm())
                            .hashAlgorithmDesc(evaluationDO.getHashAlgorithmDesc())
                            .sequenceAlgorithm(evaluationDO.getSequenceAlgorithm())
                            .sequenceAlgorithmDesc(evaluationDO.getSequenceAlgorithmDesc())
                            .otherAlgorithmDesc(evaluationDO.getOtherAlgorithmDesc())
                            .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getUseAlgorithm 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    public SystemSituationBO getSystemSituation(@NonNull SystemDO systemDO) {
        try {
            var businessContent = evaluationContentService.findBusinessCondition(systemDO.getId());
            NetworkTopologyDTO topologyDTO = evaluationContentService.findNetworkTopology(systemDO.getId());
            return SystemSituationBO.builder()
                    .businessFileDir(FileManage.getBusinessContentDir(systemDO.getId()))
                    .businessCondition(businessContent.getContent())
                    .networkTopology(NetworkTopologyBO.builder()
                            .topologyDir(FileManage.getNetworkTopologyDir(systemDO.getId()))
                            .zoneSummary(topologyDTO.getZoneSummary())
                            .topologyPicture(topologyDTO.getTopologyPicture())
                            .zoneIllustrate(topologyDTO.getZoneIllustrate())
                            .build())
                    .passwordCondition(PasswordConditionMapper.INSTANCE.toPasswordConditionBO(
                            evaluationContentService.findPasswordCondition(systemDO.getId())))
                    .systemAsset(reportAssetDataService.getSystemAssetData(systemDO))
                    .pwdServiceTable(docDataService.getPwdServiceTable(systemDO.getId()))
                    .securityThreatTable(this.getSecurityThreatTable(systemDO.getId()))
                    .lastEvaluation(this.getLastEvaluation(systemDO.getId()))
                    .build();
        } catch (RuntimeException rEx) {
            log.error("{}/getSystemSituation 获取数据出错：{}", this.getClass().getName(), rEx.getMessage());
            throw rEx;
        }
    }

    private List<SecurityThreatReportBO> getSecurityThreatTable(String systemId) {
        List<SecurityThreatDTO> securityThreats = this.securityThreatService.findSecurityThreat(systemId);
        if (CollectionUtils.isEmpty(securityThreats)) {
            return securityThreatTableByEmpty();
        }
        return securityThreats.stream()
                .map(item -> SecurityThreatReportBO.builder()
                        .number(item.getCode())
                        .type(item.getType())
                        .classify(item.getName())
                        .build())
                .toList();
    }

    private List<SecurityThreatReportBO> securityThreatTableByEmpty() {
        return List.of(SecurityThreatReportBO.builder()
                .number("")
                .type("")
                .classify("")
                .build());
    }

    private LastEvaluationBO getLastEvaluation(String systemId) {
        LastEvaluationDTO lastEvaluation = evaluationContentService.findLastEvaluation(systemId);
        LastEvaluationBO evaluationBO = LastEvaluationBO.builder()
                .count(lastEvaluation.getCount())
                .notFirst(lastEvaluation.getNotFirst())
                .build();
        if (lastEvaluation.getNotFirst()) {
            evaluationBO.setLastYear(String.valueOf(lastEvaluation.getLastDate().getYear()));
            evaluationBO.setLastMonth(String.valueOf(lastEvaluation.getLastDate().getMonthValue()));
            evaluationBO.setLastDay(String.valueOf(lastEvaluation.getLastDate().getDayOfMonth()));
            evaluationBO.setUnit(lastEvaluation.getUnit());
            evaluationBO.setConclusion(lastEvaluation.getConclusion());
            evaluationBO.setScore(lastEvaluation.getScore());
        }
        return evaluationBO;
    }

}